using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Runtime.Serialization.Formatters.Soap;

namespace MessageLibrary
{
    public class Client : IDisposable
    {
        #region Data Members

        string username;
        DateTime logOnTime;
        TcpClient tcp;
        NetworkStream stream;
        SoapFormatter sf = new SoapFormatter();
        bool running;

# endregion


        

        /// <summary>
        /// Class containing the user data
        /// </summary>
        /// <param name="tcp">tcp client</param>
        /// <param name="name">user name</param>
        /// <param name="time">time the user logged on to server</param>
        public Client(TcpClient tcpC)
        {
            tcp = tcpC;
            stream = tcp.GetStream();
            logOnTime = DateTime.Now;
            running = true;
            
           

        }
        # region Propeties
        /// <summary>
        /// Value indicating wether this client is 
        /// capable of listening and sending messages
        /// </summary>
        public bool Running
        {
            get { return running; }
        }
        /// <summary>
        /// Time the user logged in
        /// </summary>
        public DateTime LogOnTime
        {
            get { return logOnTime; }
        }
        /// <summary>
        /// user name
        /// </summary>
        public String UserName
        {
            get { return username; }
            set 
            {
                if (value!=string.Empty) username = value;
            }
        }
        /// <summary>
        /// Returns the clients IP Address
        /// </summary>
        public string Address
        {
            get { return tcp.Client.RemoteEndPoint.ToString(); }
        }

        # endregion


        #region IDisposable Members

        void IDisposable.Dispose()
        {
            stream.Dispose();
            tcp.Close();
            
            
        }

        #endregion
        # region delegates and events

        public delegate void MessageEventHandler(Client c, AbsMessage msg);
        public delegate void FinishedEventHandler(Client c);
        /// <summary>
        /// An event that occurs when a 
        /// new message is recieved
        /// </summary>
        public event MessageEventHandler NewMessage;
        /// <summary>
        /// An event that occurs when 
        /// the client has been disconnected
        /// </summary>
        public event FinishedEventHandler Finished;

        #endregion


        /// <summary>
        /// starts listening for messages on one thread
        /// and starts a checking thread on another
        /// </summary>
        public void Start()
        {
            running = true;
            Thread t = new Thread(new ThreadStart(StartListening));
            t.IsBackground = true;
            t.Start();

            Thread t1 = new Thread(new ThreadStart(StartCheck));
            t1.IsBackground = true;
            t1.Start();

        }
        /// <summary>
        /// stops the client's listening thread
        /// </summary>
        public void Stop()
        {
            running = false;
        }

        /// <summary>
        /// Contains the listenign loop for
        /// new messages for this client
        /// </summary>
        private void StartListening()
        {
            //Le loop
            while (tcp.Connected && running)
            {
                if (stream.DataAvailable && NewMessage!=null)
                {
                    object o = sf.Deserialize(stream);
                    NewMessage(this, (AbsMessage)o);
                    
                }
                Thread.Sleep(100);
            }
            //ending the connection
            running = false;
            stream.Dispose();
            tcp.Close();

            //raises an event that informs the client stopped listening
            if (Finished != null) { Finished(this); }

        }

        /// <summary>
        /// send a message using the client's stream
        /// </summary>
        /// <param name="msg">the messgae to send</param>
        public void SendMessage(AbsMessage msg)
        {
            //checks if sending is possible
            if (msg ==null || running==false)
            {
                return;
            }
            //sends the messgae
            try
            {
                sf.Serialize(stream, msg);
                stream.Flush();
            }
                
            catch (System.IO.IOException exp)
            {
                //if an IO Exception was thrown than the
                //client could not send the message becasue
                //the stream was lost
                running = false;                
                return;
            }
            catch (Exception exp)
            {
                //catches an unexpected exception
                string txt = string.Format("Could not send message to {0} becasue:\n{1}", username, exp.Message);
                System.Windows.Forms.MessageBox.Show(txt, exp.Source);
                return;
            }

        }
        /// <summary>
        /// starts a sending loop for a ststus check message
        /// to ensure the connection wasn't lost
        /// </summary>
        private void StartCheck()
        {
            StatusCheckMessage scm = new StatusCheckMessage(username);
            while (running)
            {
                Thread.Sleep(3000);
                SendMessage(scm);
            }
        }

    }
}
